#pragma once
#include "SkrSystem/advanced_input.h"
#include "SkrCore/memory/memory.h"
#include "SkrBase/atomic/atomic.h"
#include "SkrContainers/span.hpp"

namespace skr
{
namespace input
{

struct CommonInputReadingProxy {
    virtual void release(struct CommonInputReading* ptr) SKR_NOEXCEPT = 0;
};

struct SKR_SYSTEM_API CommonInputReading {
    CommonInputReading(CommonInputReadingProxy* pPool, struct CommonInputDevice* pDevice) SKR_NOEXCEPT;
    virtual ~CommonInputReading() SKR_NOEXCEPT;

    void add_ref()
    {
        skr_atomic_fetch_add_relaxed(&ref_count, 1);
    }

    int release()
    {
        skr_atomic_fetch_add_relaxed(&ref_count, -1);
        const auto rc = skr_atomic_load_acquire(&ref_count);
        if (rc == 0)
        {
            pool->release(this);
        }
        return rc;
    }

    virtual uint32_t   GetKeyState(uint32_t stateArrayCount, InputKeyState* stateArray) SKR_NOEXCEPT = 0;
    virtual bool       GetMouseState(InputMouseState* state) SKR_NOEXCEPT                            = 0;
    virtual uint64_t   GetTimestamp() const SKR_NOEXCEPT                                             = 0;
    virtual EInputKind GetInputKind() const SKR_NOEXCEPT                                             = 0;

    void Fill(InputReading** output)
    {
        if (output)
        {
            *output = (InputReading*)this;
            this->add_ref();
        }
    }

    SAtomicU32                ref_count = 0;
    CommonInputReadingProxy*  pool      = nullptr;
    struct CommonInputDevice* device    = nullptr;
};

struct SKR_SYSTEM_API CommonInputDevice {
    CommonInputDevice(struct CommonInputLayer* pLayer) SKR_NOEXCEPT;
    virtual ~CommonInputDevice() SKR_NOEXCEPT;

    virtual void Tick() SKR_NOEXCEPT = 0;

    virtual span<const EInputKind> ReportKinds() const SKR_NOEXCEPT                = 0;
    virtual bool                   SupportKind(EInputKind kind) const SKR_NOEXCEPT = 0;

    virtual EInputResult GetCurrentReading(EInputKind kind, InputReading** out_reading) SKR_NOEXCEPT                           = 0;
    virtual EInputResult GetNextReading(InputReading* reference, EInputKind kind, InputReading** out_reading) SKR_NOEXCEPT     = 0;
    virtual EInputResult GetPreviousReading(InputReading* reference, EInputKind kind, InputReading** out_reading) SKR_NOEXCEPT = 0;

    CommonInputLayer* layer = nullptr;
};

struct SKR_SYSTEM_API CommonInputLayer : public InputLayer {
    virtual ~CommonInputLayer() SKR_NOEXCEPT;
};

} // namespace input
} // namespace skr